from enum import Enum
from ..data_structures.base_structures import *
from ..enums.pp_enum import *
from tqdm import tqdm
from ..config.config import *
import math


class FactorAnalyzerComponents(Enum):
    AnalyseTime = 'analyse_time'
    AnalyseConsumption = 'analyse_consumption'
    AnalyseCrypticity = "analyse_crypticity"

    def analyse_time_with_water(cell: HexCell) -> float:
        """
        考虑水体时，应当同时考虑用户输入的配置有：涉水深度。
        假设当前格网的水体深度为a,车辆的涉水深度是b(前提是b>a)。若车辆的基础速度为v,这时应当对v进行修正,
        假设修正系数为m,
        修正方法为:v'=m*v,
        m的计算方法为:
        m=1,(a<b/4时,此时水体深度比车辆的涉水深度1/4还要小,几乎可以忽略);
        m=1/(1+(a-b/4)/b),(b/4<a<b,水体深度比车辆的涉水深度的1/4要大,这个时候就会对速度有一定影响了)
        """
        # 读取用户配置——车辆涉水深度
        vehicle_wading_depth = PPConfig.get_config("VehicleWadingDepth")
        if vehicle_wading_depth is None:
            raise Exception("请配置车辆涉水深度")
        # 读取网格的水体属性类中的水深数据
        cell_water_attribute: Attribute = cell.get_attribute(Water)
        if cell_water_attribute is None:
            raise Exception("网格的水体属性类为空")
        water_depth = cell_water_attribute.get_sub_attribute(Water.Depth)
        if water_depth is None:
            raise Exception("网格的水体属性类中的水深数据为空")
        # 计算修正因子
        m = 0
        if water_depth.get_value() < vehicle_wading_depth / 4:
            m = 1
        elif vehicle_wading_depth / 4 < water_depth.get_value() < vehicle_wading_depth:
            m = 1 / (1 + (water_depth.get_value() - vehicle_wading_depth / 4) / vehicle_wading_depth)
        return m

    def analyse_time_with_vegetable(cell: HexCell) -> float:
        """
        考虑植被时，应当同时考虑用户输入的配置有：车辆的长宽高。
        植被间距：S，单位面积内的植株密度为   D=1/S^2
        假设当前格网的植被密度为D,车辆的宽度为b，（单位为m）。
        （前提是植被类型为草、农作物、灌木时可以通过，对于其他植被，进行判断若S>b+1时，可通行）
        若车辆的基础速度为v,这时应当对v进行修正,
        假设修正系数k,
        修正方法为:v'=v*k,
        k的计算方法为:
        类别为草的影响系数设为1；灌木、耕地的影响系数设为0.7，株距大于b+1小于b+3的林地类别K设为0.5；
        株距大于等于b+3的林地的影响系数设为1
        返回k
        """
        # 读取用户配置——车辆长宽高
        vehicle_width = PPConfig.get_config("VehicleWidth")
        if vehicle_width is None:
            raise Exception("请配置车辆宽度（VehicleWidth）")

        # 获取植被属性
        cell_vegetation_attribute: Attribute = cell.get_attribute(Vegetation)
        if cell_vegetation_attribute is None:
            raise Exception("网格的植被属性类为空")

        # 获取植被类型
        veg_type_attr = cell_vegetation_attribute.get_sub_attribute(Vegetation.Type)
        if veg_type_attr is None:
            raise Exception("植被类型数据为空")
        veg_type = veg_type_attr.get_value()

        # 获取植被密度（D = 1/S^2）或株距 S
        density_attr = cell_vegetation_attribute.get_sub_attribute(Vegetation.Density)
        if density_attr is None:
            raise Exception("植被密度数据为空")
        D = density_attr.get_value()
        if D <= 0:
            raise Exception("植被密度无效，必须大于0")
        S = 1 / (D ** 0.5)  # 由密度反推株距

        # 根据植被类型和株距计算修正系数 k
        k = 1.0
        if veg_type == AttributeType.GRASS.value:
            k = 1.0
        elif veg_type == AttributeType.PLOWLAND.value or veg_type == AttributeType.SHRUBWOOD.value:
            k = 0.7
        elif veg_type == AttributeType.FOREST.value:
            if S > vehicle_width + 1 and S < vehicle_width + 3:
                k = 0.5
            elif S >= vehicle_width + 3:
                k = 1.0
            # else:
            #     # S <= vehicle_width+1 时，林地过密，不可通行
            #     k = 0.0
        else:
            # 其他类型暂时视为可通行
            k = 1.0
        base_velocity = 60
        time_weight = 1 / (k * base_velocity) * 100

        return time_weight

    def analyse_time_with_dem(cell: HexCell) -> float:
        """
        分析DEM,考虑坡度、地形起伏度对速度的影响
        """
        # 读取格网dem属性
        cell_dem_attribute: Attribute = cell.get_attribute(DemAttribute)
        if cell_dem_attribute is None:
            raise Exception("格网DEM属性类为空")
        # 读取坡度
        slope = cell_dem_attribute.get_sub_attribute(DemAttribute.Slope)
        if slope is None:
            raise Exception("格网DEM属性类中的坡度数据为空")
        # 读取地形起伏度
        curv = cell_dem_attribute.get_sub_attribute(DemAttribute.Relief)
        if curv is None:
            raise Exception("格网DEM属性类中的地形起伏度数据为空")
        # 读取用户配置——坡度和地形起伏度阈值
        if not PPConfig.has_config("SlopeThreshold"):
            raise Exception("请配置坡度阈值")
        if not PPConfig.has_config("ReliefThreshold"):
            raise Exception("请配置地形起伏度阈值")
        slope_threshold = PPConfig.get_config("SlopeThreshold")
        curvature_threshold = PPConfig.get_config("ReliefThreshold")
        # 计算修正因子
        # TODO:未建立模型
        m = 1
        return m

    def analyse_time_with_building(cell: HexCell) -> float:
        """
        计算建筑物影响的时间因子
        """
        # 获取车辆质量
        vehicle_mass = PPConfig.get_config("Vehicle.Mass")
        if vehicle_mass is None:
            raise Exception("请配置车辆重量")
        # 获取车辆速度
        vehicle_velocity = PPConfig.get_config("Vehicle.Speed")
        if vehicle_velocity is None:
            raise Exception("请配置车辆速度")
        # 读取网格的建筑物属性
        cell_building_attribute: Attribute = cell.get_attribute(Building)
        if cell_building_attribute is None:
            raise Exception("网格的建筑物属性类为空")
        # 获取墙体强度等级
        building_hardness = cell_building_attribute.get_sub_attribute(Building.Hardness)
        if building_hardness is None:
            raise Exception("网格的建筑物属性类中的墙体强度等级数据为空")
        hardness = building_hardness.get_value()
        # 根据强度等级计算系数
        if hardness <= 1:
            c_factor = 0.3
        elif 1 < hardness <= 2:
            c_factor = 1.5
        elif 2 < hardness <= 3:
            c_factor = 10
        else:
            c_factor = 25
        # K 常数（源码里写死，后续可拓展）
        k_constant = 1000000.0
        # 计算建筑物被破坏阈值 DT
        dt = c_factor * k_constant
        # 计算车辆冲击动能 ET
        et = 0.5 * vehicle_mass * (vehicle_velocity ** 2)
        # 计算时间因子（已通过通行性检查，所以et > dt）
        time_factor = 1 + et / dt
        return time_factor

    def analyse_time_with_soil(cell: HexCell) -> float:
        """
        基于土壤力学模型计算时间因子
        返回: 时间因子
        """
        # 新增雨量-硬度折减系数表
        RAIN_REDUCTION_FACTORS = {
            "Asphalt": [1.0, 1.0, 1.0, 1.0],
            "DryClay": [1.0, 0.8, 0.5, 0.2],
            "WetClay": [1.0, 0.9, 0.7, 0.5],
            "Sand": [1.0, 0.95, 0.85, 0.7],
            "Loam": [1.0, 0.9, 0.75, 0.6],
            "Gravel": [1.0, 0.98, 0.95, 0.9]
        }
        RAIN_LEVELS = [0, 10, 25, 50]
        soil_attr = cell.get_attribute(Soil)
        if not soil_attr:
            raise Exception("网格的土壤属性为空")

        # 获取输入参数
        rainfall = PPConfig.get_config("Rainfall")  # mm, 默认0

        # 获取土壤属性
        hardness_attr = soil_attr.get_sub_attribute(Soil.Hardness)
        if not hardness_attr:
            raise Exception("土壤硬度属性为空")
        ci = hardness_attr.get_value()  # kPa

        type_attr = soil_attr.get_sub_attribute(Soil.Type)
        if not type_attr:
            raise Exception("土壤类型属性为空")
        st = type_attr.get_value()

        # 计算雨量折减系数
        rain_level = 0
        for i, level in enumerate(RAIN_LEVELS):
            if rainfall >= level:
                rain_level = i

        if st not in RAIN_REDUCTION_FACTORS:
            st = "Loam"  # 使用默认土壤类型
        k = RAIN_REDUCTION_FACTORS[st][rain_level]
        ci_effective = ci * k

        # 土壤参考值
        soil_refs = {
            "Asphalt": {"ci_ref": 3000, "mu_ref": 0.02},
            "DryClay": {"ci_ref": 2000, "mu_ref": 0.06},
            "WetClay": {"ci_ref": 300, "mu_ref": 0.25},
            "Sand": {"ci_ref": 500, "mu_ref": 0.15},
            "Loam": {"ci_ref": 1500, "mu_ref": 0.05},
            "Gravel": {"ci_ref": 2000, "mu_ref": 0.06}
        }

        # 获取当前土壤类型的参考值
        ref = soil_refs.get(st, soil_refs["Loam"])

        ci_effective = ci * k
        if ci_effective <= 0:
            ci_effective = 1  # 设置一个最小有效值

        # 计算滚动阻力系数
        mu_roll = ref["mu_ref"] * math.sqrt(ref["ci_ref"] / ci_effective)
        mu_base = 0.05  # 基准滚动阻力系数

        # 计算时间因子和油耗因子
        time_factor = mu_roll / mu_base

        return time_factor

    def analyse_time(map: Map):
        """分析时间因子"""
        for cell in tqdm(map.cells.values(), desc="分析时间因子"):
            cell: HexCell
            if cell.get_attribute(Water) is not None:
                time_factor = FactorAnalyzerComponents.analyse_time_with_water(cell)
                cell.weights.append(TimeWeight(time_factor))
            elif cell.get_attribute(Soil) is not None:
                time_factor = FactorAnalyzerComponents.analyse_time_with_soil(cell)
                cell.weights.append(TimeWeight(time_factor))
            elif cell.get_attribute(Building) is not None:
                time_factor = FactorAnalyzerComponents.analyse_time_with_building(cell)
                cell.weights.append(TimeWeight(time_factor))
            elif cell.get_attribute(Vegetation) is not None:
                time_factor = FactorAnalyzerComponents.analyse_time_with_vegetable(cell)
                cell.weights.append(TimeWeight(time_factor))
            elif cell.get_attribute(DemAttribute) is not None:
                influence_factor = FactorAnalyzerComponents.analyse_time_with_dem(cell)
                cell.weights.append(TimeWeight(time_factor))

            # elif cell.road_type is not RoadType.NOWAY.value:
            #     time_weight = inflence[AttributeType.ROAD] * base_velocity
            #     cell.weights.append(TimeWeight(time_weight))

    def analyse_consumption_with_water(cell: HexCell) -> float:
        """
        考虑水体时，应当同时考虑用户输入的配置有：水底地质。不同水底地质类型的情况下，车辆的能耗会有所差异
        """
        # 读取网格的水体属性类中的水底地质
        cell_bottom_geology_attribute: Attribute = cell.get_attribute(Water)
        if cell_bottom_geology_attribute is None:
            raise Exception("网格的水体属性类中的水底地质数据为空")
        m = 0

        if cell_bottom_geology_attribute == "1号种类水底地质":
            m = 0.98
        elif cell_bottom_geology_attribute == "2号种类水底地质":
            m = 0.95
        return m

    def analyse_consumption_with_vegetable(cell: HexCell) -> float:
        """
        考虑植被时，应当同时考虑用户输入的配置有：车辆宽度。不同植被类型的情况下，车辆的能耗会有所差异
        假设当前格网的植被密度为D,车辆的宽度为b，（单位为m）。
        （前提是植被类型为草、农作物、灌木时可以通过，对于其他植被，进行判断若S>b+1时，可通行）
        若车辆的基础速度为c,这时应当对c进行修正,
        假设修正系数k,
        修正方法为:c'=c*k,
        k的计算方法为:
        类别为草的影响系数设为1.1；灌木、耕地的影响系数设为1.2，株距大于等于b+4的林地类别K设为1；
        株距大于b+1小于b+4的林地的影响系数设为1+(8-s)/8
        返回k
        """
        # 读取用户配置——车辆宽度
        vehicle_width = PPConfig.get_config("VehicleWidth")
        if vehicle_width is None:
            raise Exception("请配置车辆宽度（VehicleWidth）")

        # 获取植被属性
        cell_vegetation_attribute: Attribute = cell.get_attribute(Vegetation)
        if cell_vegetation_attribute is None:
            raise Exception("网格的植被属性类为空")

        # 获取植被类型
        veg_type_attr = cell_vegetation_attribute.get_sub_attribute(Vegetation.Type)
        if veg_type_attr is None:
            raise Exception("植被类型数据为空")
        veg_type = veg_type_attr.get_value()

        # 获取植被密度（D=1/S^2）或株距 S
        density_attr = cell_vegetation_attribute.get_sub_attribute(Vegetation.Density)
        if density_attr is None:
            raise Exception("植被密度数据为空")
        D = density_attr.get_value()
        if D <= 0:
            raise Exception("植被密度无效，必须大于0")
        S = 1 / (D ** 0.5)  # 由密度反推株距

        # 根据植被类型和株距计算修正系数 k
        k = 1.0
        if veg_type == AttributeType.GRASS.value:  # 草地
            k = 1.1
        elif veg_type == AttributeType.PLOWLAND.value or veg_type == AttributeType.SHRUBWOOD.value:  # 耕地、灌木
            k = 1.2
        elif veg_type == AttributeType.FOREST.value:  # 林地
            if S >= vehicle_width + 4:
                k = 1.0
            elif vehicle_width + 1 < S < vehicle_width + 4:
                k = 1 + (8 - S) / 8
            else:
                k = 0.0  # 表示不可通行
        else:
            k = 1.0  # 其他类型默认可通行
        return k

    def analyse_consumption_with_dem(cell: HexCell) -> float:
        """
        分析DEM,考虑坡度、地形起伏度对速度的影响
        """
        # 读取格网dem属性
        cell_dem_attribute: Attribute = cell.get_attribute(DemAttribute)
        if cell_dem_attribute is None:
            raise Exception("格网DEM属性类为空")
        # 读取坡度
        slope = cell_dem_attribute.get_sub_attribute(DemAttribute.Slope)
        if slope is None:
            raise Exception("格网DEM属性类中的坡度数据为空")
        # 读取地形起伏度
        curv = cell_dem_attribute.get_sub_attribute(DemAttribute.Curvature)
        if curv is None:
            raise Exception("格网DEM属性类中的地形起伏度数据为空")
        # 读取用户配置——坡度和地形起伏度阈值
        config: PPConfig = PPConfig()
        if not config.has_config("SlopeThreshold"):
            raise Exception("请配置坡度阈值")
        if not config.has_config("CurvatureThreshold"):
            raise Exception("请配置地形起伏度阈值")
        slope_threshold = config.get_config("SlopeThreshold")
        curvature_threshold = config.get_config("CurvatureThreshold")
        # 计算修正因子
        # TODO:未建立模型
        m = 1
        return m

    def analyse_consumption_with_building(cell: HexCell) -> float:
        """
        计算建筑物影响的油耗因子
        """
        # 读取网格的建筑物属性
        cell_building_attribute: Attribute = cell.get_attribute(Building)
        if cell_building_attribute is None:
            raise Exception("网格的建筑物属性类为空")
        # 获取墙体强度等级
        building_hardness = cell_building_attribute.get_sub_attribute(Building.Hardness)
        if building_hardness is None:
            raise Exception("网格的建筑物属性类中的墙体强度等级数据为空")
        hardness = building_hardness.get_value()
        # 根据强度等级计算系数
        if hardness <= 1:
            c_factor = 0.3
        elif 1 < hardness <= 2:
            c_factor = 1.5
        elif 2 < hardness <= 3:
            c_factor = 10
        else:
            c_factor = 25
        # 计算油耗因子（基于建筑物强度）
        consumption_factor = 1 + c_factor * 0.1
        return consumption_factor

    def analyse_consumption_with_soil(cell: HexCell) -> float:
        """
        基于土壤力学模型计算油耗因子
        返回: 油耗因子
        """
        # 新增雨量-硬度折减系数表
        RAIN_REDUCTION_FACTORS = {
            "Asphalt": [1.0, 1.0, 1.0, 1.0],
            "DryClay": [1.0, 0.8, 0.5, 0.2],
            "WetClay": [1.0, 0.9, 0.7, 0.5],
            "Sand": [1.0, 0.95, 0.85, 0.7],
            "Loam": [1.0, 0.9, 0.75, 0.6],
            "Gravel": [1.0, 0.98, 0.95, 0.9]
        }
        RAIN_LEVELS = [0, 10, 25, 50]

        soil_attr = cell.get_attribute(Soil)
        if not soil_attr:
            raise Exception("网格的土壤属性为空")

        # 获取输入参数
        rainfall = PPConfig.get_config("Rainfall")  # mm, 默认0

        # 获取土壤属性
        hardness_attr = soil_attr.get_sub_attribute(Soil.Hardness)
        if not hardness_attr:
            raise Exception("土壤硬度属性为空")
        ci = hardness_attr.get_value()  # kPa

        type_attr = soil_attr.get_sub_attribute(Soil.Type)
        if not type_attr:
            raise Exception("土壤类型属性为空")
        st = type_attr.get_value()

        # 计算雨量折减系数
        rain_level = 0
        for i, level in enumerate(RAIN_LEVELS):
            if rainfall >= level:
                rain_level = i

        if st not in RAIN_REDUCTION_FACTORS:
            st = "Loam"  # 使用默认土壤类型
        k = RAIN_REDUCTION_FACTORS[st][rain_level]

        ci_effective = ci * k

        # 土壤参考值
        soil_refs = {
            "Asphalt": {"ci_ref": 3000, "mu_ref": 0.02},
            "DryClay": {"ci_ref": 2000, "mu_ref": 0.06},
            "WetClay": {"ci_ref": 300, "mu_ref": 0.25},
            "Sand": {"ci_ref": 500, "mu_ref": 0.15},
            "Loam": {"ci_ref": 1500, "mu_ref": 0.05},
            "Gravel": {"ci_ref": 2000, "mu_ref": 0.06}
        }

        # 获取当前土壤类型的参考值
        ref = soil_refs.get(st, soil_refs["Loam"])

        # 计算滚动阻力系数

        ci_effective = ci * k
        if ci_effective <= 0:
            ci_effective = 1  # 设置一个最小有效值

        # 计算滚动阻力系数
        mu_roll = ref["mu_ref"] * math.sqrt(ref["ci_ref"] / ci_effective)

        mu_base = 0.05  # 基准滚动阻力系数

        # 计算时间因子和油耗因子
        fuel_factor = mu_roll / mu_base
        return fuel_factor

    def analyse_consumption(map: Map):
        """分析能耗因子"""
        for cell in tqdm(map.cells.values(), desc="分析能耗因子"):
            cell: HexCell
            if cell.get_attribute(Water) is not None:
                consumption_factor = FactorAnalyzerComponents.analyse_consumption_with_water(cell)
                cell.weights.append(ConsumptionWeight(consumption_factor))
            elif cell.get_attribute(Soil) is not None:
                consumption_factor = FactorAnalyzerComponents.analyse_consumption_with_soil(cell)
                cell.weights.append(ConsumptionWeight(consumption_factor))
            elif cell.get_attribute(Building) is not None:
                consumption_weight = FactorAnalyzerComponents.analyse_consumption_with_building(cell)
                cell.weights.append(ConsumptionWeight(consumption_weight))
            elif cell.get_attribute(Vegetation) is not None:
                consumption_factor = FactorAnalyzerComponents.analyse_consumption_with_vegetable(cell)
                cell.weights.append(ConsumptionWeight(consumption_factor))
            elif cell.get_attribute(DemAttribute) is not None:
                consumption_factor = FactorAnalyzerComponents.analyse_consumption_with_dem(cell)
                cell.weights.append(ConsumptionWeight(consumption_factor))

    def analyse_crypticity(map: Map):
        # 默认参数
        default_config = {
            "FOREST": {"canopy": 0.6, "H": 3.0},
            "SHRUBWOOD": {"D": 0.2, "H": 2.0},
            "PLOWLAND": {"D": 0.15, "H": 1.5}
        }

        # 用户配置覆盖默认参数
        # if config is not None:
        #     for k, v in config.items():
        #         if k in default_config:
        #             default_config[k].update(v)

        # 用原始 map 的类动态创建一个新 map（保持一致性）
        new_map = map.__class__()

        for index, cell in map.cells.items():
            concealed = False

            # --- Vegetation checks ---
            veg_attr = cell.get_attribute(Vegetation)
            if veg_attr is not None:
                veg_type = veg_attr.get_sub_attribute(Vegetation.Type)
                canopy = veg_attr.get_sub_attribute(Vegetation.CanopyClosure)
                H = veg_attr.get_sub_attribute(Vegetation.AverageHeight)
                D = veg_attr.get_sub_attribute(Vegetation.Density)

                veg_type = veg_type.get_value() if veg_type else None
                canopy = float(canopy.get_value()) if canopy else None
                H = float(H.get_value()) if H else None
                D = float(D.get_value()) if D else None

                if veg_type == AttributeType.FOREST.value:
                    thresholds = default_config["FOREST"]
                    if canopy is not None and H is not None and canopy >= thresholds["canopy"] and H >= thresholds["H"]:
                        concealed = True

                elif veg_type == AttributeType.SHRUBWOOD.value:
                    thresholds = default_config["SHRUBWOOD"]
                    if D is not None and H is not None and D >= thresholds["D"] and H >= thresholds["H"]:
                        concealed = True

                elif veg_type == AttributeType.PLOWLAND.value:
                    thresholds = default_config["PLOWLAND"]
                    if D is not None and H is not None and D >= thresholds["D"] and H >= thresholds["H"]:
                        concealed = True

            # --- Building rule ---
            bld_attr = cell.get_attribute(Building)
            if bld_attr is not None:
                bld_count = 0
                for n_hid in cell.neighbors:
                    n_cell = map.cells.get(n_hid, None)
                    if n_cell and n_cell.get_attribute(Building) is not None:
                        bld_count += 1
                if bld_count >= 3:
                    concealed = True

            # 如果满足隐蔽条件，把 cell 拷贝到 new_map
            if concealed:
                new_cell = cell.__class__(index)  # 保持 cell 类型一致
                new_cell.attributes = cell.attributes.copy()
                new_cell.neighbors = cell.neighbors.copy()
                new_map.cells[index] = new_cell

        # 遍历new_map，按照new_map中的索引修改cell的隐蔽度因子
        indexes = []
        for cell in new_map.cells.items():
            indexes.add(cell.index)
        for cell in map.cells.items():
            if cell.index in indexes:
                cell: HexCell
                crypticity_factor = Crypticity(1)
                cell.get_attribute(Weight).add_sub_attribute(crypticity_factor)

